package com.db.utils;

import com.db.entity.Book;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.BooleanUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.RandomUtils;

import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
import java.util.function.Function;

/**
 * @author dzh
 * @since 2024/10/22
 */
public abstract class FileReaderUtils {

    public static void dealBatch(String path, int batchSize, Function<List<Book>, Boolean> func) {
        AtomicInteger counter = new AtomicInteger();
        List<String> titles = new ArrayList<>();
        List<Book> books = new ArrayList<>();
        FileReaderUtils.dealLine(path, titles::add, (str) -> {
            if (StringUtils.isBlank(str)) {
                return true;
            }
            counter.getAndIncrement();
            Book book = Book.builder()
                    .title(titles.get(titles.size() - 1))
                    .line(str)
                    .id(counter.get())
                    .build();
            books.add(book);
            if (books.size() % batchSize == 0) {
                long start = System.currentTimeMillis();
                // 提交数据
                Boolean apply = func.apply(books);
                String title = books.get(books.size() - 1).getTitle();
                long ms = System.currentTimeMillis() - start;
                System.out.printf("数量=%s title=%s 耗时=%sms%n", counter.get(), title, ms);
                books.clear();
                return apply;
            }
            return true;
        });
        if (!books.isEmpty()) {
            func.apply(books);
        }
        System.out.printf("finish 数量=%d%n", counter.get());
    }

    public static void dealLine(String path, Consumer<String> title, Function<String, Boolean> fun) {
        dealLine(path, title, fun, StandardCharsets.UTF_8);
    }
    public static void dealLine(String path, Consumer<String> title, Function<String, Boolean> fun, Charset charset) {
        File file = new File(path);
        File[] files = file.listFiles();
        assert files != null;
        for (File f : files) {
            try {
                boolean next = dealOneFile(f, title, fun, charset);
                if (!next) {
                    return;
                }
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }

    public static boolean dealOneFile(File f, Consumer<String> title, Function<String, Boolean> fun, Charset charset) throws IOException {
        title.accept(f.getName());
        String str = FileUtils.readFileToString(f, charset);
        String[] split = str.split("\r\n");
        for (String line : split) {
            for (String s : splitByLength(line, 50)) {
                Boolean apply = fun.apply(s);
                if (apply == null || !apply) {
                    return false;
                }
            }
        }
        return true;
    }

    public static String[] splitByLength(String str, int len) {
        assert len > 0;
        int size = (str.length() + len - 1) / len;
        String[] result = new String[size];
        for (int i = 0; i < result.length; i++) {
            result[i] = StringUtils.substring(str, i * len, i * len + len);
        }
        return result;
    }

    public static String random(char start, int size, int range) {
        StringBuilder result = new StringBuilder();
        for (int i = 0; i < size; i++) {
            result.append((char) (RandomUtils.nextInt(0, range) + start));
        }
        return result.toString();
    }
    
    public static  <T> void dealBatch(List<T> list, int batchSize, Function<List<T>, Boolean> func) {
        if (list.size() < batchSize) {
            func.apply(list);
        }
        int start = 0;
        int end;
        while (list.size() > start) {
            end = start + batchSize;
            end = Math.min(end, list.size());
            Boolean next = func.apply(list.subList(start, end));
            if (BooleanUtils.isNotTrue(next)) {
                return;
            }
            start = end;
        }
    }
}
